Refleksja w Rubim

Struktura dostęp do informacji o typie, polach i metodach

Dynamiczne ładowanie klas


dr inż. Aleksander Smywiński-Pohl

apohllo@agh.edu.pl

http://apohllo.pl/dydaktyka/programowanie-obiektowe

konsultacje: wtorek 15:30 - 18:00, pokój 4.61

Pytanie 1

Do czego najbardziej mogą przydać się nam zagnieżdżone klasy i interfejsy?

Pytanie 2

Czy Java Developer powinien się zaznajamiać głęboko z wyrażeniami lambda wprowadzonymi w Java 8 pomimo spadków wydajności przy używaniu paralelizmu, utrudnionego debugowania ze względu na wydłużone "stack trace"?

Pytanie 3

Jeśli w klasie wewnętrznej zostanie zadeklarowana zmienna tego samego typu i o tej samej nazwie co w klasie otaczającej to czy przesłoni ona tę z otaczającej?

Pomoc wbudowana w REPLa

In [ ]:
[1] pry(main)> require 'dry/types'
=> true
[2] pry(main)> ls Dry
constants: Configurable  Container  Core  Equalizer  Logic  Types
Dry.methods: Equalizer
[3] pry(main)> ls Dry::Equalizer
constants: Methods
Object.methods: yaml_tag
Module.methods: constants  nesting  used_modules
[4] pry(main)> ? Dry::Equalizer#constants

From: variable.c (C Method):
Owner: Module
Visibility: public
Signature: constants(*arg1)
Number of lines: 12

Returns an array of the names of the constants accessible in
mod. This includes the names of constants in any included
modules (example at start of section), unless the inherit
parameter is set to false.

The implementation makes no guarantees about the order in which the
constants are yielded.

  IO.constants.include?(:SYNC)        #=> true
  IO.constants(false).include?(:SYNC) #=> false

Also see Module::const_defined?.

Informacja o typie

In [29]:
puts 1.class()
puts "Ala ma kota".class()
Integer
String

Weryfikacja przynależności do typu

In [30]:
object = "Ala ma kota"

if(object.is_a?(String))
    puts "Obiekt jest łańcuchem znaków"
end
Obiekt jest łańcuchem znaków
In [31]:
object = "Ala ma kota"

if(String === object)
    puts "Obiekt jest łańcuchem znaków"
end
Obiekt jest łańcuchem znaków
In [32]:
object = [1,2,3]

case(object)
when String
  puts "Obiekt jest łańuchem znaków"
when Array
  puts "Obiekt jest tablicą"
end
Obiekt jest tablicą

Informacje o hierarchii klas

In [41]:
class Animal
end

class Mammal < Animal
end

class Cat < Mammal
end

class Bat < Mammal
end

puts Cat.new
puts Cat.new.class
puts Cat.new.class.superclass
puts Cat.new.class.class
puts Cat.new.class.class.class.superclass.superclass.superclass.superclass
#<Cat:0x000055874badf160>
Cat
Mammal
Class

In [42]:
def ancestors(klass, chain=[])
  klass.superclass ? ancestors(klass.superclass, chain + [klass]) : chain.reverse
end

puts ancestors(Bat)
[Object, Animal, Mammal, Bat]
In [43]:
class Whale < Mammal
end

class Reptile < Animal
end

class Snake < Reptile
end
In [44]:
[1,2,3].zip(["a", "b", "c"]).to_a
Out[44]:
[[1, "a"], [2, "b"], [3, "c"]]
In [45]:
[1,2,3,4,5,6].each_cons(2).to_a
Out[45]:
[[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]]
In [46]:
[1,2,3,4,5].
  zip(["a", "b", "c", "d", "e"]).
  each_cons(2).
  each{|(a1, b1), (a2,b2)| puts "#{a1} - #{b1} : #{a2} - #{b2}"}
1 - a : 2 - b
2 - b : 3 - c
3 - c : 4 - d
4 - d : 5 - e
In [47]:
def commonAncestor(klass1, klass2)
  ancestors(klass1).zip(ancestors(klass2)).each_cons(2) do |(grandparent1, grandparent2), (parent1, parent2)|
    return grandparent1 if parent1 != parent2 
  end
end
Out[47]:
:commonAncestor
In [48]:
puts commonAncestor(Bat, Whale)
puts commonAncestor(Snake, Bat)
puts commonAncestor(Snake, Reptile)
puts commonAncestor(1.class, Bat)
Mammal
Animal
Reptile
Object

Dynamiczne ładowanie klas

In [49]:
class Car
  def go
    puts "Brum brum"
  end
end

class Airplane
  def go
    puts "Bzzzzz"
  end
end

class Rocket
  def go
    puts "BUUUUUUM"
  end
end
Out[49]:
:go
In [50]:
["Car", "Car", "Rocket", "Airplane"].each do |classname|
  Class.const_get(classname).new.go
end
Brum brum
Brum brum
BUUUUUUM
Bzzzzz
Out[50]:
["Car", "Car", "Rocket", "Airplane"]

Refleksja

In [51]:
puts Car.new.public_methods.sort
[:!, :!=, :!~, :<=>, :==, :===, :=~, :__binding__, :__id__, :__send__, :byebug, :class, :clone, :debugger, :define_singleton_method, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :go, :hash, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :methods, :nil?, :object_id, :pretty_inspect, :pretty_print, :pretty_print_cycle, :pretty_print_inspect, :pretty_print_instance_variables, :private_methods, :protected_methods, :pry, :public_method, :public_methods, :public_send, :remote_byebug, :remove_instance_variable, :respond_to?, :send, :singleton_class, :singleton_method, :singleton_methods, :taint, :tainted?, :tap, :to_enum, :to_json, :to_s, :to_yaml, :trust, :untaint, :untrust, :untrusted?]
In [52]:
puts (Car.new.public_methods - 1.public_methods).sort
[:go]
In [53]:
class Bike
  def initialize
    @speed = 0
  end

  def go
    @speed = 10
  end

  def stop
    @speed = 0
  end

  def riding?
    @speed > 0
  end
end
Out[53]:
:riding?
In [54]:
puts Bike.new.instance_variables
[:@speed]

Dynamiczne wywoływanie metod

In [55]:
bike = Bike.new
puts bike.public_send("riding?")
bike.public_send("go")
puts bike.public_send("riding?")
false
true
In [57]:
bike = Bike.new
#bike.public_send("initialize")
bike.send("initialize")
Out[57]:
0

Dynamiczny dostęp do pól

In [62]:
bike = Bike.new
puts bike.instance_variable_get("@speed")
bike.instance_variable_set("@speed", 10)
puts bike.instance_variable_get("@speed")
0
RuntimeError: Don't do it!
(pry):419:in `instance_variable_set'
(pry):424:in `<main>'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/pry-0.11.3/lib/pry/pry_instance.rb:355:in `eval'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/pry-0.11.3/lib/pry/pry_instance.rb:355:in `evaluate_ruby'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/pry-0.11.3/lib/pry/pry_instance.rb:323:in `handle_line'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/pry-0.11.3/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/pry-0.11.3/lib/pry/pry_instance.rb:242:in `catch'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/pry-0.11.3/lib/pry/pry_instance.rb:242:in `block in eval'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/pry-0.11.3/lib/pry/pry_instance.rb:241:in `catch'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/pry-0.11.3/lib/pry/pry_instance.rb:241:in `eval'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/iruby-0.3/lib/iruby/backend.rb:66:in `eval'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/iruby-0.3/lib/iruby/backend.rb:12:in `eval'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/iruby-0.3/lib/iruby/kernel.rb:87:in `execute_request'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/iruby-0.3/lib/iruby/kernel.rb:47:in `dispatch'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/iruby-0.3/lib/iruby/kernel.rb:37:in `run'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/iruby-0.3/lib/iruby/command.rb:70:in `run_kernel'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/iruby-0.3/lib/iruby/command.rb:34:in `run'
/home/apohllo/.rvm/gems/ruby-2.4.2/gems/iruby-0.3/bin/iruby:5:in `<top (required)>'
/home/apohllo/.rvm/gems/ruby-2.4.2/bin/iruby:23:in `load'
/home/apohllo/.rvm/gems/ruby-2.4.2/bin/iruby:23:in `<main>'
/home/apohllo/.rvm/gems/ruby-2.4.2/bin/ruby_executable_hooks:15:in `eval'
/home/apohllo/.rvm/gems/ruby-2.4.2/bin/ruby_executable_hooks:15:in `<main>'
In [61]:
class Bike
  def instance_variable_set(name, value)
    raise "Don't do it!"
  end
end
Out[61]:
:instance_variable_set
In [ ]:
bike = Bike.new
puts bike.instance_variable_get("@speed")
bike.instance_variable_set("@speed", 10)
puts bike.instance_variable_get("@speed")

Dynamiczne proxy

In [63]:
class Wrapper
  def initialize(object)
    @object = object
    @invocations = []
  end

  def method_missing(*arguments)
    @invocations << arguments 
    @object.public_send(*arguments)
  end

  def replay
    @invocations.each do |arguments|
      @object.public_send(*arguments)
    end
  end
end
Out[63]:
:replay
In [64]:
wrapper = Wrapper.new([])
wrapper << 1
wrapper << 2
wrapper << 3
puts wrapper.to_a.join(", ")

wrapper.replay

puts wrapper.to_a.join(", ")
1, 2, 3
1, 2, 3, 1, 2, 3